#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""

Just for testing:
    >>> from StringIO import StringIO

Required imports:

    >>> from tack.lib import string_app, default_env
    >>> from tack.lib.formreader import formreader

It's pretty easy to use.

    >>> app = formreader(string_app('eggs'))
    >>> app
    <function app...>

    >>> app(default_env(), lambda a, b:None)
    ['eggs']


    >>> app = formreader(string_app('ham'))
    >>> env = default_env({'wsgi.input':StringIO('eggs=12&spam=false'),
    ...                    'REQUEST_METHOD':'POST'})
    >>> app(env, lambda a, b:None)
    ['ham']

    >>> form = env['form']
    >>> form
    {'eggs': '12', 'spam': 'false'}

    >>> form['eggs']
    '12'

Now to  test multi-part parsing

    >>> app = formreader(string_app('ham'))
    >>> env = default_env({'wsgi.input':file('tests/formreader.test.data'),
    ...                    'REQUEST_METHOD':'POST',
    ...                    'CONTENT_LENGTH': '504',
    ...                    'CONTENT_TYPE':'multipart/form-data; boundary=---------------------------9304734438996164231261310786'})
    >>> app(env, lambda a, b:None)
    ['ham']

    >>> form = env['form']
    >>> form
    {'fileupload': 'A file', 'box1': 'We use this file because multi-line', 'box2': "strings didn't work in doc-tests."}
    

Now to  test multi-part parsing, keeping the file name

    >>> app = formreader(string_app('ham'), keep_name=True)
    >>> env = default_env({'wsgi.input':file('tests/formreader.test.data'),
    ...                    'REQUEST_METHOD':'POST',
    ...                    'CONTENT_LENGTH': '504',
    ...                    'CONTENT_TYPE':'multipart/form-data; boundary=---------------------------9304734438996164231261310786'})
    >>> app(env, lambda a, b:None)
    ['ham']

    >>> form = env['form']
    >>> form['fileupload']
    ('thetest.file', <cStringIO.StringO object at 0x...>)

    >>> form['box1']
    'We use this file because multi-line'

    >>> form['box2']
    "strings didn't work in doc-tests."

Testing multiple entries of same name, e.g. check boxes.

    >>> app = formreader(string_app('ham'))
    >>> env = default_env({'wsgi.input':StringIO('eggs=One&eggs=Two&eggs=Three'),
    ...                    'REQUEST_METHOD':'POST'})
    >>> app(env, lambda a, b:None)
    ['ham']

    >>> form = env['form']
    >>> form
    {'eggs': ['One', 'Two', 'Three']}

"""
import cgi
from tack.lib import copy_appattrs


def formreader(app, target_key='form', keep_blanks=True, keep_name=False):
    def inner(environ, start_response):
        if target_key not in environ:
            fp = environ['wsgi.input']
            form = cgi.FieldStorage(fp=fp, environ=environ,
                                    keep_blank_values=keep_blanks)
            environ[target_key] = data = {}
            for key in form.keys():
                obj = form[key]
                if isinstance(obj, list):
                    # Work around for multipart data, where even single
                    # values are put in an array
                    if len(obj) == 1:
                        value = obj[0].value
                    else:
                        value = [v.value for v in obj]
                elif obj.file:
                    # Another quirk in cgi.py.  If a form is multi-part,
                    # even "normal" form attributes are put in cStringIO
                    # instances.disposition If it has a content-type header, then
                    # it is assumed that it must be a non-file form
                    # element.  Really annoying.
                    if 'content-type' not in obj.headers:
                        value = obj.file.read()
                    else:
                    # If we are keeping the name, then that means we
                    # care about the name and are probably writing out
                    # the file somewhere, and it is probably a rather
                    # large file, so we'll read it into memory when
                    # we write it, no need to read() it all now.
                        if keep_name:
                            value = (obj.filename, obj.file)
                        else:
                            value = obj.file.read()
                else:
                    value = obj.value
                data[key] = value

        return app(environ, start_response)
    return copy_appattrs(app, inner)
